using System;
using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
public class WebSocketManager : MonoBehaviour
{
    #region Singleton
    public static WebSocketManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
    #endregion

    #region Configuration
    [Header("Connection Settings")]
    [SerializeField] private string _serverUrl = "ws://api.question.game.xmnk.cn/ws/";
    [SerializeField] private int _reconnectDelay = 5; // 秒
    [SerializeField] private int _maxReconnectAttempts = 3;
    #endregion

    private ClientWebSocket _webSocket;
    private CancellationTokenSource _cts;
    private bool _isManualDisconnect;
    private int _currentReconnectAttempts;

    public event Action OnConnected;
    public event Action<string> OnMessageReceived;
    public event Action<string> OnDisconnected;

    #region Public Methods
    public async void Connect()
    {
        if (_webSocket != null && _webSocket.State == WebSocketState.Open)
        {
            Debug.LogWarning("WebSocket is already connected");
            return;
        }

        _isManualDisconnect = false;
        _currentReconnectAttempts = 0;
        await InternalConnect();
    }

    public async void Disconnect()
    {
        _isManualDisconnect = true;
        await SafeCloseConnection("Client requested disconnect");
    }

    public async Task SendMessageAsync(string message)
    {
        if (_webSocket?.State != WebSocketState.Open)
        {
            Debug.LogError("Cannot send message - connection not open");
            return;
        }

        try
        {
            Debug.Log($"发送WebSocket消息: {message}"); // 添加发送消息日志
            byte[] bytes = Encoding.UTF8.GetBytes(message);
            await _webSocket.SendAsync(
                new ArraySegment<byte>(bytes),
                WebSocketMessageType.Text,
                true,
                _cts.Token
            );
        }
        catch (Exception ex)
        {
            Debug.LogError($"Send message failed: {ex.Message}");
            HandleDisconnection(ex.Message);
        }
    }

    #endregion

    #region Connection Management
    private async Task InternalConnect()
    {
        try
        {
            _cts?.Dispose();
            _cts = new CancellationTokenSource();
            
            _webSocket = new ClientWebSocket();
            await _webSocket.ConnectAsync(new Uri(_serverUrl), _cts.Token);

            // Ensure dispatcher exists before using it
            if (UnityMainThreadDispatcher.Instance != null)
            {
                UnityMainThreadDispatcher.Instance.Enqueue(() => 
                {
                    OnConnected?.Invoke();
                    Debug.Log("WebSocket connected successfully");
                });
            }
            else
            {
                Debug.LogError("MainThreadDispatcher not found - cannot dispatch to main thread");
            }

            _ = ReceiveMessagesAsync();
            _currentReconnectAttempts = 0;
        }
        catch (Exception ex)
        {
            Debug.LogError($"Connection failed: {ex.Message}");
            HandleDisconnection(ex.Message);
        }
    }

    private async Task ReceiveMessagesAsync()
    {
        var buffer = new byte[4096];
        try
        {
            while (_webSocket.State == WebSocketState.Open)
            {
                var result = await _webSocket.ReceiveAsync(
                    new ArraySegment<byte>(buffer), 
                    _cts.Token
                );

                if (result.MessageType == WebSocketMessageType.Close)
                {
                    await SafeCloseConnection("Server initiated close");
                    break;
                }

                string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                Debug.Log($"收到WebSocket消息: {message}"); // 添加消息打印
                
                UnityMainThreadDispatcher.Instance.Enqueue(() => 
                {
                    OnMessageReceived?.Invoke(message);
                });
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"Receive error: {ex.Message}");
            HandleDisconnection(ex.Message);
        }
    }
    #endregion

    #region Error Handling & Reconnection
    private async void HandleDisconnection(string reason)
    {
        if (_isManualDisconnect) return;

        UnityMainThreadDispatcher.Instance.Enqueue(() => 
        {
            OnDisconnected?.Invoke(reason);
            Debug.LogWarning($"Connection lost: {reason}");
        });

        if (_currentReconnectAttempts < _maxReconnectAttempts)
        {
            _currentReconnectAttempts++;
            Debug.Log($"Attempting reconnect ({_currentReconnectAttempts}/{_maxReconnectAttempts})...");
            
            await Task.Delay(_reconnectDelay * 1000);
            await InternalConnect();
        }
        else
        {
            Debug.LogError("Max reconnect attempts reached");
        }
    }

    private async Task SafeCloseConnection(string reason)
    {
        try
        {
            if (_webSocket?.State == WebSocketState.Open)
            {
                await _webSocket.CloseAsync(
                    WebSocketCloseStatus.NormalClosure,
                    reason,
                    CancellationToken.None
                );
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"Error closing connection: {ex.Message}");
        }
        finally
        {
            _webSocket?.Dispose();
            UnityMainThreadDispatcher.Instance.Enqueue(() => 
            {
                OnDisconnected?.Invoke(reason);
            });
        }
    }
    #endregion

    private void OnDestroy()
    {
        _ = SafeCloseConnection("Manager destroyed");
        _cts?.Cancel();
        _cts?.Dispose();
    }
}
